using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;
using NPOI.SS.UserModel;

namespace HEFramework
{
    internal class SheetTable
    {
        private string fileName;
        private List<Property> properties;
        private ISheet sheet;
        private string dataSavePath;
        private string codeSavePath;
        private bool appendData;
        private DataTableSettingScriptableObject data = DataTableSettingScriptableObject.Instance;

        public SheetTable(string _fileName, ISheet _sheet)
        {
            this.fileName = _fileName;
            properties = new List<Property>();
            dataSavePath = DataTableTemplate.Inst.GetDataFilePath(_sheet.SheetName);
            codeSavePath = DataTableTemplate.Inst.GetCodeFilePath(_sheet.SheetName);
            appendData = false;
            AddPropertiesFromISheet(_sheet);
        }

        public void Join(string _fileName, ISheet _sheet)
        {
            this.fileName = _fileName;
            JoinPropertiesFromISheet(_sheet);
        }

        public void SaveData(LangOutput _langoutput)
        {
            if (sheet == null)
            {
                return;
            }

            StringBuilder sb = new StringBuilder();
            IRow row;
            string firstCell, firstValue;
            int lastRowNum = sheet.LastRowNum;
            for (int r = data.PropertyStartRow; r <= lastRowNum; r++)
            {
                row = sheet.GetRow(r);
                firstCell = XssfUtils.GetCellString(row, 0);
                firstValue = XssfUtils.GetCellString(row, properties[0].Column);
                if (data.IsIgnoreColumnWhichFirstRowIsHashTag && firstCell == "#")
                {
                }
                else if (data.IsIgnoreRowWhichFirstPropertyIsEmpty && firstValue == "")
                {
                }
                else
                {
                    ParsePropertiesValueFormRow(row, sb, _langoutput);
                    if (r != lastRowNum)
                    {
                        sb.Append("\n");
                    }
                }
            }

            if (appendData)
            {
                sb.Insert(0, "\n");
                System.IO.File.AppendAllText(dataSavePath, sb.ToString());
            }
            else
            {
                System.IO.File.WriteAllText(dataSavePath, sb.ToString());
            }
        }

        private void ParsePropertiesValueFormRow(IRow _row, StringBuilder _sb, LangOutput _langoutput)
        {
            string key;
            string curCell;
            string firstProperyValue = "";
            for (int i = 0; i < properties.Count; i++)
            {
                // 格式替换
                curCell = XssfUtils.GetCellString(_row, properties[i].Column);
                if (curCell == "")
                {
                    curCell = properties[i].EmptyReplace();
                }

                // 检查
                if (!properties[i].Check(curCell, out string msg))
                {
                    DataTableLog.PrintCellError(fileName, sheet.SheetName, _row.RowNum, properties[i].Column, msg);
                    curCell = "";
                }

                // 写入
                if (i == 0)
                {
                    firstProperyValue = curCell;
                }

                if (curCell == "")
                {
                    _sb.Append("");
                }
                else if (properties[i].IsOutputKey)
                {
                    key = _langoutput.AddContent(fileName, sheet.SheetName, properties[i], curCell, firstProperyValue);
                    _sb.Append(key);
                }
                else if (data.IsReplaceTableAndEnterChar)
                {
                    _sb.Append(curCell.Replace("\n", "\\n").Replace("\t", "\\t"));
                }
                else
                {
                    _sb.Append(curCell);
                }

                if (i != properties.Count - 1)
                {
                    _sb.Append("\t");
                }
            }
        }


        public void SaveCode()
        {
            if (sheet == null)
            {
                return;
            }

            System.IO.File.WriteAllText(codeSavePath, DataTableTemplate.Inst.GetCodeText(fileName, sheet.SheetName, properties).ToString());
        }


        private void JoinPropertiesFromISheet(ISheet _sheet)
        {
            this.sheet = _sheet;
            string sheetName = _sheet.SheetName;

            Dictionary<string, Property> dic = new Dictionary<string, Property>();
            for (int i = 0; i < properties.Count; i++)
            {
                dic.Add(properties[i].Name, properties[i]);
            }

            List<string> firstList = XssfUtils.GetRowData(_sheet, 0);
            List<string> settingList = XssfUtils.GetRowData(_sheet, data.PropertySettingRow);
            List<string> nameList = XssfUtils.GetRowData(_sheet, data.PropertyNameRow);
            List<string> typeList = XssfUtils.GetRowData(_sheet, data.PropertyTypeRow);
            List<string> noteList = XssfUtils.GetRowData(_sheet, data.PropertyNoteRow);
            if (nameList != null)
            {
                for (int i = 0; i < nameList.Count; i++)
                {
                    string first = GetCellString(firstList, i);
                    string setting = GetCellString(settingList, i);
                    string name = GetCellString(nameList, i);
                    string type = GetCellString(typeList, i);
                    string note = GetCellString(noteList, i);

                    if (name == "")
                    {
                    }
                    else if (data.IsIgnoreColumnWhichFirstRowIsHashTag && first == "#")
                    {
                    }
                    else
                    {
                        if (!dic.ContainsKey(name) ||
                            !dic[name].ChangeSheet(name, type, note, setting, i))
                        {
                            break;
                        }

                        dic.Remove(name);
                    }
                }
            }

            if (dic.Count != 0)
            {
                DataTableLog.PrintSheetError(fileName, sheetName, "已存在同名sheet, 并且配置不一致");
                this.sheet = null;
            }
        }

        private void AddPropertiesFromISheet(ISheet _sheet)
        {
            this.sheet = _sheet;
            string sheetName = _sheet.SheetName;
            List<string> firstList = XssfUtils.GetRowData(_sheet, 0);
            List<string> settingList = XssfUtils.GetRowData(_sheet, data.PropertySettingRow);
            List<string> nameList = XssfUtils.GetRowData(_sheet, data.PropertyNameRow);
            List<string> typeList = XssfUtils.GetRowData(_sheet, data.PropertyTypeRow);
            List<string> noteList = XssfUtils.GetRowData(_sheet, data.PropertyNoteRow);
            if (nameList != null)
            {
                for (int i = 0; i < nameList.Count; i++)
                {
                    string first = GetCellString(firstList, i);
                    string setting = GetCellString(settingList, i);
                    string name = GetCellString(nameList, i);
                    string type = GetCellString(typeList, i);
                    string note = GetCellString(noteList, i);
                    if (name == "")
                    {
                    }
                    else if (data.IsIgnoreColumnWhichFirstRowIsHashTag && first == "#")
                    {
                    }
                    else if (!AddProperty(name, type, note, setting, i))
                    {
                        this.sheet = null;
                        break;
                    }
                }
            }

            if (properties.Count == 0)
            {
                this.sheet = null;
                DataTableLog.PrintSheetError(fileName, sheetName, "表格中并没有属性可导出");
            }
        }

        private bool AddProperty(string _name, string _type, string _note, string _setting, int _column)
        {
            // 检查名字
            int propertyIndex = GetPropertyIndex(_name);
            if (propertyIndex != -1)
            {
                DataTableLog.PrintCellError(fileName, sheet.SheetName, data.PropertyNameRow, _column, "属性名字重复");
                return false;
            }

            // 创建对象
            bool isArray = _type.EndsWith(data.PropertyArrEndWith);
            string typeName = GetPropertyTypeName(_type, isArray);

            // 创建对象
            Property property = GetEnumProperty(typeName);
            if (property == null)
            {
                property = GetNormalProperty(typeName);
            }

            if (property == null)
            {
                DataTableLog.PrintCellError(fileName, sheet.SheetName, data.PropertyTypeRow, _column, "属性类型错误");
                return false;
            }

            // 创建初始化，并加入列表
            PropertySetting[] propertySettings;
            if (_setting == "")
            {
                propertySettings = new PropertySetting[0];
            }
            else
            {
                PropertySetting propertySetting;
                string[] settingArr = _setting.Split(data.PropertyArrSplitChar);
                propertySettings = new PropertySetting[settingArr.Length];
                for (int i = 0; i < settingArr.Length; i++)
                {
                    if (!Enum.TryParse(_setting, out propertySetting))
                    {
                        DataTableLog.PrintCellError(fileName, sheet.SheetName, data.PropertySettingRow, _column, "属性配置错误");
                        propertySettings = new PropertySetting[0];
                        break;
                    }
                    else
                    {
                        propertySettings[i] = propertySetting;
                    }
                }
            }

            property.Init(_name, _type, _note, _setting, _column, propertySettings, isArray);
            properties.Add(property);
            return true;
        }

        private Property GetEnumProperty(string _typeName)
        {
            EnumPropertyT property = null;
            Type type = data.Asmb.GetType(_typeName);
            if (type != null)
            {
                property = new EnumPropertyT();
                string[] names = Enum.GetNames(type);
                if (names != null && names.Length != 0)
                {
                    property.SetEnum(type, _typeName);
                }
            }

            return property;
        }

        private Property GetNormalProperty(string _typeName)
        {
            Type[] constructorParamTypes = new Type[0];
            object[] constructorParam = new Type[0];
            ConstructorInfo info = Type.GetType("HEFramework." + _typeName + "Property")?.GetConstructor(constructorParamTypes);
            Property property = info?.Invoke(constructorParam) as Property;
            return property;
        }

        private string GetPropertyTypeName(string _type, bool _isArray)
        {
            string typeName;
            if (_isArray)
            {
                typeName = _type.Remove(_type.Length - data.PropertyArrEndWith.Length, data.PropertyArrEndWith.Length);
            }
            else
            {
                typeName = _type;
            }

            return typeName;
        }

        private int GetPropertyIndex(string _name)
        {
            for (int i = 0; i < properties.Count; i++)
            {
                if (properties[i].Name == _name)
                {
                    return i;
                }
            }

            return -1;
        }

        private string GetCellString(List<string> _rowData, int _column)
        {
            return _rowData == null || _column >= _rowData.Count ? "" : _rowData[_column];
        }

        private void ClearText()
        {
            System.IO.File.WriteAllText(dataSavePath, "");
            System.IO.File.WriteAllText(codeSavePath, "");
        }

        /// <summary>
        /// 首字母大写
        /// </summary>
        /// <param name="_input"></param>
        /// <returns></returns>
        private string FirstCharToUpper(string _input)
        {
            if (String.IsNullOrEmpty(_input))
            {
                return "";
            }
            else
            {
                return _input[0].ToString().ToUpper() + _input.Substring(1);
            }
        }
    }
}